// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CImplNodeBinding.
//   $Id$

#include "stdafx.h"
#include "nodeBinding.h"
#include "XFormsXFormsElement.h"
#include "BoundNode.h"
#include "implBind.h"
#include "model.h"
#include "implGroup.h"
#include "SingleNodeBinding.h"
#include "XFormsElement.h"
#include "output.h"
#include "FP_Definitions.h"
#include "WaitWithMessageLoop.h"

CImplNodeBinding::CImplNodeBinding()
:m_pBind(NULL)
,m_bInParentContext(false)
,m_bPartInitialized(false)
,m_lpwszControlID(0)
,m_bCanBindToValue(false)
,m_hInRebuild(0)
{
		m_hInRebuild = CreateMutex(0,FALSE, "XPORT_FP_mutex_NodeBinding_in_Rebuild");
};

CImplNodeBinding::CImplNodeBinding(CXFormsElement * pElement)
:CFormControl(pElement)
,m_bInParentContext(false)
,m_pBind(NULL)
,m_bPartInitialized(false)
,m_lpwszControlID(0)
,m_bCanBindToValue(false)
,m_hInRebuild(0)
{
	m_hInRebuild = CreateMutex(0,FALSE, "XPORT_FP_mutex_NodeBinding_in_Rebuild");
};
// If exceptions occur during the deletion of reliant controls, they may be ignored for debugging purposes
//	i.e. to allow you to find the real cause of the problem without having to stop at this ASSERT
//	a squillion times, by defining IGNORE_RELIANT_CONTROL_DELETION_ERRORS as 1
#define IGNORE_RELIANT_CONTROL_DELETION_ERRORS 1

// If exceptions occur during the deletion of reliant controls, and you wish to allow them to 
//	report themselves, instead of being caught in the catch(...) in the deletion loop,
//	remove the undef below.

#define CATCH_RELIANT_CONTROL_DELETION_ERRORS_AT_SOURCE 1
#undef CATCH_RELIANT_CONTROL_DELETION_ERRORS_AT_SOURCE

CImplNodeBinding::~CImplNodeBinding()
{
	//WaitForSingleObject(m_hInRebuild,100);
	CloseHandle(m_hInRebuild);
	m_hInRebuild = 0;
	try
	{
		int iTop = m_vertices.GetUpperBound();
		CVertex * vx = 0;
		CBoundNode * pBN = 0;
		
		if(m_pXForms && m_pXForms->canContinue())
		{
			for(int i = 0; i <= iTop; ++i)
			{	
				try
				{
					vx =  m_vertices.GetAt(i);
					if(vx)
					{
						pBN = vx->m_pInstanceNode;
						if(pBN)
							pBN->RemoveTarget(this);
					}
				}
				catch(...)
				{
					AtlTrace("!formsplayer.dll: CImplNodeBinding Dtor Error clearing individual vertex.\n");
					ASSERT(0);
				}

			}
		}
	}
	catch(...)
	{
		AtlTrace("!formsplayer.dll: CImplNodeBinding Dtor Error clearing vertices. ControlType:'%S', XPath:'%S', @bind='%S'\n",m_sControlType,*m_pbsXPathExpression,m_sBind);
		ASSERT(0);
	}

	if(m_bInParentContext && m_pParentImpl)
	{
		// Bug 465 - Get the context parent of the element.
		CImplNodeBinding * pParentContext = NULL;
		getContextParent(&pParentContext);
		
		//Remove from the parentImpl's list of bound controls
		if(dynamic_cast<CImplNodeBinding*>(pParentContext))
			((CImplNodeBinding*)pParentContext)->RemoveControl(this);

	}

/*	if(m_pParentImpl && dynamic_cast<CImplNodeBinding*>(m_pParentImpl))
		((CImplNodeBinding*)m_pParentImpl)->RemoveControl(this);
*/
	if(m_bInParentContext && m_pParent && dynamic_cast<CImplNodeBinding*>(m_pParent))
	{
		//Remove from the parentImpl's list of bound controls
		((CImplNodeBinding*)m_pParent)->RemoveControl(this);
	}

	while(!m_ReliantControls.IsEmpty())
	{
#ifndef CATCH_RELIANT_CONTROL_DELETION_ERRORS_AT_SOURCE
		try
		{
#endif
			CImplNodeBinding * pRelControl  = m_ReliantControls.RemoveHead();
			if(pRelControl)
			{
				pRelControl->RequestDeleteAtEarliestConvenience();
			}
#ifndef CATCH_RELIANT_CONTROL_DELETION_ERRORS_AT_SOURCE
		}
		catch(...)
		{
			ASSERT(IGNORE_RELIANT_CONTROL_DELETION_ERRORS);
		}
#endif

	}

	//This call is redundant, it does nothing more than the loop 
	//	at the top.
	//ClearBindings();

	
	//If the control is destroyed before it is completely inititalizes, it 
	//	will probably be in the form's initPending list.  Remove it, otherwise
	//	we may see no-mans-land errors.
	if(!m_bInitializeDone && m_pXForms)
	{
		POSITION pos = m_pXForms->m_lstInitPending.Find(this);
		if(pos)
			m_pXForms->m_lstInitPending.RemoveAt(pos);
	}

	if(m_pBind)
	{
		m_pBind->RemoveControl(this);
	}
	if(m_lpwszControlID) {
		delete[] m_lpwszControlID;
		m_lpwszControlID = 0;
	}

}


HRESULT CImplNodeBinding::formControlInitialize()
{	
	FireEvent(EVENT_FORMCONTROLINITIALIZE);
	if(!m_bInParentContext)
		buildDependencies();
	//Just gives the front end a chance to do any first-time processing.
	HRESULT hr = S_OK;
	if(m_pXFormsElement)
	{
		hr = m_pXFormsElement->formControlInitialize();
		
		if(m_pXForms && m_pXForms->m_XFConfig.FormBrandingDebugInfo())
		{
			CComBSTR sDebugInfo;
			sDebugInfo.Attach(GenerateDebugDisplayInfo());
			m_pXFormsElement->put_XPathExpression(sDebugInfo);
		}

	}
	return hr;
}

void CImplNodeBinding::buildDependencies()
{
	//inserts vertices for this binding 
	//	into the masterdependencylist.
	if(m_pBind)
	{
	//do nothing, pBind will have done it for us.	
	}
	else if(*m_pbsXPathExpression && !isEmpty(*m_pbsXPathExpression)&& m_bisBound && m_pModel)
	{
		CIdResolver * pResolver = GetIdResolver();

		
		INT_PTR p = m_vertices.GetUpperBound();
		
		for(INT_PTR i = 0;i <= p; ++i)
		{
			CBoundNode * pBN = m_vertices[i]->m_pInstanceNode;
			if(pBN)
				pBN->RemoveTarget(this);
		}

		m_vertices.RemoveAll();
		
		ClearBindings();					

		IXMLDOMNode * pContext = NULL;
		GetEvaluationContext(&pContext);
		if(pContext)
		{

#ifdef _DEBUG
			if(false)
			{
				CComBSTR s;
				pContext->get_xml(&s);
				::MessageBoxW(0,s,0,0);
			}
#endif
			HRESULT hr;
			hr = m_pModel->addVertex
			(
				
				pContext,
				CComBSTR(*m_pbsXPathExpression),
				m_bApplyFirstNodeRule,
				CVertex::EType::Instance,
				NULL,
				&m_vertices
				,pResolver
			);

			if(SUCCEEDED(hr))
			{
				long l = m_vertices.GetSize();
				if(l == 0)
				{
					onBindToNothing();
				}
				else
				{
					bool bHasBound = false;
					long lOldNodesSize = m_OldNodes.GetSize();
					bool b_loopReceiveStateChangeEvents = false;
					for(--l; l >=0;--l)
					{
						CBoundNode * pBoundNode = m_vertices.GetAt(l)->m_pInstanceNode;
						if(pBoundNode)
						{
							bHasBound = true;
							
							//If this node was present in the last binding (pre refresh)
							//	don't receive  state change events
							
							//if there is a new node in the nodeset that was not present
							//	prior to refresh, then state-change events should fire.
							if(!(l < lOldNodesSize && m_OldNodes.GetAt(l) == pBoundNode))
								b_loopReceiveStateChangeEvents = true;
							if(b_loopReceiveStateChangeEvents && l==0)
								pBoundNode->addTarget(this,false);	
							else
								pBoundNode->addTarget(this,true);	
						}
						else
						{
							m_vertices.RemoveAt(l);
						}
					}

					if(!bHasBound)
					{
						onBindToNothing();
					}
				}
			}
			else if(m_pXForms->canContinue())
			{
				//Only give a binding exception when one has not already been encountered.
				m_pModel->onBindingException(*m_pbsXPathExpression,L"nodeset or ref attribute must evaluate to a node.");
			}
		}
		::safeRelease(pContext);
		ConfirmBindings();
	}
	else
	{
		//If it is not bound, then ensure that the vertex list is empty.
		ClearBindings();
		ConfirmBindings();
	}

	if(m_pXForms && m_pXForms->canContinue())
	{
		buildChildDependencies();
	}


}

void CImplNodeBinding::buildChildDependencies()
{
	//Always run buildDependencies on the reliant controls,
	//	even if we have no node.  This means that any 
	//	contained elements that rely on this node for context
	//	will have their bindings nullified too.
	POSITION pos = m_ReliantControls.GetHeadPosition();
	try
	{
		while(pos && !m_ReliantControls.IsEmpty() && m_pXForms)
		{
			if(m_hInRebuild)
			{
				DWORD dwWait = WAIT_OBJECT_0;//xport::WaitWithMessageLoop(m_hInRebuild,10);
				if(dwWait == WAIT_OBJECT_0)
				{
					//try/catch to prevent locking of mutex in case of exception
					try
					{
						CImplNodeBinding * pChildBinding = m_ReliantControls.GetNext(pos);
						if(pChildBinding)
							pChildBinding->buildDependencies();
					}
					catch(...)
					{
						//ASSERT(0);
					}
				//	ReleaseMutex(m_hInRebuild);
				}
				else
				{
				//	ReleaseMutex(m_hInRebuild);
					break;
				}

			}
			else
				break;
		}
	}
	catch(...)
	{
		//ASSERT(0);
	}
}

HRESULT CImplNodeBinding::doInitialize()
{
	//objects that rely on their parent 
	//	need a chance to initialize,
	//	however, they should only refresh
	//	under the control of the parent.
	HRESULT hr = S_OK;

	hr = formControlInitialize();

	// RMB - Added to write the navindex and accesskey to the UI Object.
    // This is not the best location for this.
	// PRB - I don't even think it needs to be done.  
	//	AccessKey and navindex are handled by the host document,
	//	and the viewlink controls are opaque in this sense.
/*
	BSTR sVal;
	getNavIndex(&sVal);
	if(sVal)
		m_pXFormsElement->put_navindex(sVal);
			
	getAccessKey(&sVal);
	
	if(sVal)
		m_pXFormsElement->put_accesskey(sVal);
*/	
	//Tell the reliant controls to initialize.
	if(SUCCEEDED(hr))
	{
		POSITION pos = m_ReliantControls.GetHeadPosition();
		CImplNodeBinding * pReliantControl = 0;
		while(pos)
		{
			pReliantControl = m_ReliantControls.GetNext(pos);
			//There can be no situation in which a reliant control is 
			//	initialized before the control on which it relies.
			//	It is possible, that after a submit, initializedone
			//	is set to true by the original initialisation.
			//	set the flag to false here to force reinitialization.
			pReliantControl->m_bInitializeDone = false;
			hr = pReliantControl->initialize(true);
			if(FAILED(hr))
				break;
		}
	}
	
	if((!m_bInParentContext))
	{
		//nothing else will refresh you.
		hr = refresh();
	}
	else if(m_pXForms->m_bDocumentInitialised)
	{
		//It may be the case that this is a node inserted into a control that has  already been
		//	initialised, or it may be the case that this is being initialiseed as part of the 
		//	parent control's initialisation.
		//If the latter, don't refresh, as it will only refresh again during the unwind.
		CFormControl * pParentFC = dynamic_cast<CFormControl *>(m_pParentImpl);
		if(!pParentFC)
			pParentFC = dynamic_cast<CFormControl *>(m_pParent);
		//If parent initialisation is in progress, then this will be refreshed
		//	as a result of parent initialisation anyway
		if(!pParentFC || !pParentFC->InitializeInProgress())
			hr = refresh();
	}
	else
	{
		if
		(
			(m_pParent && static_cast<CFormControl*>(m_pParent)->m_bInitializeDone)
			||
			(m_pParentImpl && static_cast<CFormControl*>(m_pParentImpl)->m_bInitializeDone)
		)
		{
		//	buildDependencies();
			hr = refresh();
		}
	}

	// Because some controls can be added after the Initalisation of it's model. For example 
	// item in a repeat, there style is not copied to the UIScript. If the control is added 
	// after the initalisation the flag m_bLateInitalise is set which indicates that the copy 
	// style need to be call. Bug 187.
	
	if(isLateInitalisedControl() && m_pXFormsElement)
		m_pXFormsElement->updateAppearance();


	return hr;

}


HRESULT CImplNodeBinding::blank()
{
	HRESULT hr = S_OK;
	POSITION pos = m_ReliantControls.GetHeadPosition();
	while(pos)
	{
		hr = m_ReliantControls.GetNext(pos)->blank();
		if(FAILED(hr))
			break;
	}
	if(SUCCEEDED(hr))
		hr = CFormControl::blank();
	return hr;
}


bool CImplNodeBinding::AddControl(CImplNodeBinding *pControl)
{
	//only enter and exit the postponement state if it is not already in it.
	bool bPostponementState = IsInDeletionPostponementState();
	if(!bPostponementState)
		EnterDeletionPostponementState();
	ASSERT(reinterpret_cast<unsigned int>(pControl) != 0xdddddddd);
		
	bool bRet = false;
	//a reliant control must be bound to the same model as this is.
	getModel();

	//If the potential control has no explicitly named model, 
	//	That model is this model.  
	if(pControl->m_sModel.Length() == 0)
		pControl->setModel(m_pModel);


	// a control can rely on another iff: 
	// *	both controls are bound, 
	// *	both controls are bound to the same model
	// *	the contained control is not root-relative.

	if(!m_bisBound)
	{
		//	If this is not a bound control (eg. button)
		//	binding should pass up to the next level.
		if(m_pParent)
		{
			bRet = static_cast<CImplNodeBinding*>(m_pParent)->AddControl(pControl);
			pControl->m_pParentImpl = m_pParent;
		}
		else if(m_pParentImpl)
		{
			bRet = static_cast<CImplNodeBinding*>(m_pParentImpl)->AddControl(pControl);
		}
	}
	else if
	(
		pControl->m_bisBound 
		&& pControl->m_pModel==m_pModel
		&& *pControl->m_pbsXPathExpression
		//&& *(pControl->m_pbsXPathExpression)[0]!='/'
	)
	{
		pControl->m_bInParentContext = true;
		m_ReliantControls.AddHead(pControl);
		if(m_vertices.GetUpperBound() != -1)
		{
			pControl->buildDependencies();
		}
		bRet = true; 
	}

	if(IsAwaitingDeletion())
	{
		bRet = false;
	}
	else
	{
		// If the model has already been initalised then updateAppearance() should be called
		// so that style is correctly applied to the control. Bug 187.
		if(m_pModel)
			if(m_pModel->isInitalised())
				m_bLateInitalise = true;
	}

	if(!bPostponementState)
		ExitDeletionPostponementState();

	return bRet;
}


HRESULT CImplNodeBinding::getModel()
{
	//could be further optimised by returning a bool corresponding
	//	to whether it has been derived from the parent or not, but that 
	//	rather assumes a knowledge of how the function is to be used
	HRESULT hr = E_FAIL;
	//Choices are: 

	if(m_pModel)
	//		I have a model, return it
		hr = S_OK;
	else if(!m_bisBound)
	{
		hr = S_FALSE;
	}
	else if(!!m_sModel)
	{
		//I have a string reference to a model, 
		//	so fetch a pointer to it.
		CImplModel * pModel = 0;
		hr = m_pXForms->getModel(m_sModel,pModel);
		setModel(pModel);
	}
	else 
	{
		//If this call occurs before ElementReady
		//	then m_sModel won't be populated yet.
		if(m_pXFormsElement && S_OK == m_pXFormsElement->get_ModelId(&m_sModel))
		{
			CImplModel * pModel = 0;
			hr = m_pXForms->getModel(m_sModel,pModel);
			setModel(pModel);			
		}
		else
		{
			// Bug 465 - The member m_pParentImpl now points to the parent xforms element
			// and not the context parent. Hence we need to crawl the tree until we find a 
			// object that we can obtain the context from.
			CImplNodeBinding * pEnclosing = 0;
			hr = getContextParent(&pEnclosing);
			if(pEnclosing)
			{
				hr = pEnclosing->getModel();
				setModel(pEnclosing->m_pModel);
			}
			else if(m_pXForms)
			{
				//If I have neither a parent nor a model name, 
				//	I must be bound to the default (first)
				//	model.
				CImplModel * pModel = 0;
				hr = m_pXForms->getModel(NULL,pModel);
				setModel(pModel);
			}	
		}
	}

	return hr;
}

HRESULT CImplNodeBinding::GetEvaluationContext(IXMLDOMNode **ppNode)
{
	//returns a node for evaluation of references in context.
	HRESULT hr = S_FALSE;
	hr = getModel();
	if(m_pModel)
	{
		if(m_bInParentContext)
		{
			if(m_pParent)
			{
				CImplSingleNodeBinding * pContextEvaluator  = 0;
				//nullify *ppNode to prevent false success after getInstanceNode
				*ppNode = 0;

				//Get the immediately enclosing binding element node.
				
				CImpl * pEnclosingNode = m_pParent;
				while(pEnclosingNode && !pContextEvaluator)
				{
					pContextEvaluator = dynamic_cast<CImplSingleNodeBinding*>(pEnclosingNode);
					if(pContextEvaluator && !pContextEvaluator->m_bisBound)
					{
						pContextEvaluator = 0;
					}
					
					if(pEnclosingNode->m_pParent)
					{
						pEnclosingNode = pEnclosingNode->m_pParent;
					}
					else
					{
						pEnclosingNode = pEnclosingNode->m_pParentImpl;
					}
				}

				if(pContextEvaluator && pContextEvaluator->m_pModel == m_pModel)
				{
					pContextEvaluator->getInstanceNode(ppNode);
					if(*ppNode)
					{
						//If a context node has been found, 
						//Do no more.
						hr = S_OK;
					}
					else
					{
						CImplOutput * pOutput = dynamic_cast<CImplOutput*>(pContextEvaluator);
						if(pOutput && !::isEmpty(pOutput->m_sValue))
						{
							//If pContextEvaluator was successfully returned, 
							//	it may not be bound to a node (eg. <output value="fn()" />)
							//	In that case, get its context.
							hr = pContextEvaluator->GetEvaluationContext(ppNode);
						}
						else
						{
							//TODO: this probably doesn't belong here, should be in a specialisation
							//TODO: I don't know if this will ever be entered,
							//		<xf:group /> has a default ref of "."
							//		<xf:group ref=""/> explicitly binds to nothing, thereby setting nothing as context for its children.
							CImplGroup * pGroup = dynamic_cast<CImplGroup *>(pContextEvaluator);
							if(pGroup && ::isEmpty(pGroup->m_sRef) && ::isEmpty(pGroup->m_sModel))
							{
								pContextEvaluator->GetEvaluationContext(ppNode);
							}
							else
							{
								*ppNode = 0;
								hr = S_FALSE;
							}
						}
					}
				}
				else
				{
					//if pContextEvaluator was not successfully returned, 
					//or its model is incompatible with this model,
					//	context is NULL (which later evaluates to the default context)

					hr = m_pModel->GetDefaultEvaluationContext(ppNode);
				}
			}
			else if(m_pParentImpl)
			{
				//TODO: This should be implemented without dynamic cast.
				//		in case we find a way of working without RTTI.
				//		I suggest that m_pParentImpl or equivalent should
				//		be a CImplSingleNodeBinding
				//		
				//If this node does rely on its parent, 
				//get the parent's node.
				
				CImplSingleNodeBinding * pContextEvaluator  = NULL;
				CImplNodeBinding * pNodeBinding  = 0;
				// Bug 465 - Get the context parent of this element
				//This While looks dangerous!
				//while(!pContextEvaluator && pNodeBinding){
					getContextParent(&pNodeBinding);
					pContextEvaluator = dynamic_cast<CImplSingleNodeBinding *> (pNodeBinding);
				//}

				if(pContextEvaluator  && pContextEvaluator->m_pModel == m_pModel)
				{
					pContextEvaluator->getInstanceNode(ppNode);
					if(*ppNode)
					{
						hr = S_OK;
					}
					else
					{
						//TODO: This should probably be handled by an interface, or property
						//	rather than a cast to output
						CImplOutput * pOutput = dynamic_cast<CImplOutput*>(pContextEvaluator);
						if(pOutput)// && pOutput->m_sValue.Length > 0)
						{
							//If pContextEvaluator was successfully returned, 
							//	it may not be bound to a node (eg. <output value="fn()" />)
							//	In that case, get its context.
							hr = pContextEvaluator->GetEvaluationContext(ppNode);
						}
						else
						{
							*ppNode = 0;
							hr = S_FALSE;
						}
					}
				}
				else if(m_pModel)
				{
					m_pModel->GetDefaultEvaluationContext(ppNode);
				}
			}
			else if(m_pModel)
			{
				//	otherwise, use the default evaluation context.
				hr = m_pModel->GetDefaultEvaluationContext(ppNode);
			}
			else
			{
				*ppNode = 0;
				hr = E_FAIL;
			}
		}
		else
		{
			hr = m_pModel->GetDefaultEvaluationContext(ppNode);	
		}
	}
	return hr;
}


void CImplNodeBinding::disconnect(CBoundNode* pNode)
{
	long l = m_vertices.GetUpperBound();
	while(l >= 0)
	{
		if(m_vertices.GetAt(l)->m_pInstanceNode == pNode)
		{
			m_vertices.RemoveAt(l);
			break;
		}
		--l;
	}
}
#include "DebugDisplayStrings.h"
BSTR CImplNodeBinding::GenerateDebugDisplayInfo(void)
{
		CStringW sDebugInfo;
		CStringW sModel("");
		if(m_pModel)
		{
			sModel = m_pModel->m_sId;
		}

		const wchar_t * const * psReadOnly = &g_wsReadWrite;
		const wchar_t * const * psRelevant = &g_wsEnabled;
		const wchar_t * const * psValid = &g_wsValid;
		if(m_vertices.GetUpperBound()!=-1)
		{
			CBoundNode * pBN = m_vertices[0]->m_pInstanceNode;
			if(pBN)
			{
				if(pBN->isReadonly())
					psReadOnly = &g_wsReadOnly;
				if(!pBN->isRelevant())
					psRelevant = &g_wsDisabled;
				if(!pBN->isValid())
					psValid = &g_wsInvalid;
			}
		}
		const wchar_t * const wsXPathType = m_bApplyFirstNodeRule?L"ref":L"nodeset";
		if(!m_sId.m_str) {  // Element has no ID
			if(m_lpwszControlID)
				delete[] m_lpwszControlID;
			m_lpwszControlID = new wchar_t[12];
			if(m_lpwszControlID)
				wcscpy(m_lpwszControlID, g_lpwszAnonymous);
		}
		sDebugInfo.Format
		(
			L"%s\nid\t\"%s\"\nmodel\t\"%s\"\n%s\t\"%s\"\n%s, %s, %s"
			,m_sControlType
			,m_sId.m_str?m_sId:m_lpwszControlID
			,sModel
			,wsXPathType
			,*m_pbsXPathExpression
			,*psRelevant
			,*psReadOnly
			,*psValid
		);
		return sDebugInfo.AllocSysString();
}


void CImplNodeBinding::setModel(CImplModel*  pModel)
{
	__super::setModel(pModel);
	//Any children that do not have a model attribute
	//	should also be changed.
	POSITION pos = m_ReliantControls.GetHeadPosition();
	while(pos)
	{
		CImplNodeBinding * pChild = m_ReliantControls.GetNext(pos);
		if(::isEmpty(pChild->m_sModel))
		{
			pChild->setModel(pModel);
		}
	}
}

BSTR CImplNodeBinding::getFullXPath(void) const
{
	return 0;
}

void CImplNodeBinding::ConfirmBindings()
{
	long lMax = m_OldNodes.GetUpperBound();
	for(long i=0;i<=lMax;++i)
	{
		CBoundNode * pBN = m_OldNodes.GetAt(i);
		if(pBN)
			pBN->Release();
	}
	m_OldNodes.RemoveAll();
}
#define IGNORE_CLEARBINDINGS_ERROR 1
void CImplNodeBinding::ClearBindings(void)
{
	//TEMP, the following line is to see if this 
	//	function is needed at all.  may cause memory leaks.
	//m_vertices.RemoveAll();
	// NOTE: This line was causing a crash in my debugger form so,
	//       in response to the previous comment, I'd say that this
	//       function is required after all [PMB 20031007]

	int iTop = m_vertices.GetUpperBound();
	CVertex * vx = 0;
	CBoundNode * pBN = 0;
	for(int i = 0; i <= iTop; ++i)
	{	
		vx =  m_vertices.GetAt(i);
		if(vx)
		{
			pBN = vx->m_pInstanceNode;
			if(pBN)
			{
				// HACK: I was getting a crash when dereferencing pBN here, during refresh
				//       processing done as a post-modal-message action after acknowledging
				//       the reporting of a binding exception. I tried to resolve it properly
				//       but it was beyond me. - phil.booth, 04-Oct-2006.
				try {
					pBN->AddRef();
					m_OldNodes.Add(pBN);
					pBN->RemoveTarget(this);
				}
				catch(...) 
				{
					AtlTrace("\n !formsPlayer.dll - ClearBindings Error");
					ASSERT(!IGNORE_CLEARBINDINGS_ERROR);
				}
			}
		}
	}
	m_vertices.RemoveAll();
}


HRESULT CImplNodeBinding::init(CImpl *pParent)
{
	HRESULT hr = S_OK;

	if(m_sBind)
		*m_pbsXPathExpression = 0;

	if(!m_bPartInitialized)
	{	
		//The rest of init cannot continue
		//	after getModel if getModel cannot
		//	return a model.  This may happen if
		//	a control occurs earlier than the 
		//	model to which it binds in document
		//	order
		//m_bPartInitialized stops this part happening again.
		hr = CFormControl::init(pParent);
		m_bPartInitialized = true;
	}
	/*
	 * Add this control to the list of form controls in the model
	 */
	//Retrieve a pointer to the model, 
	// This means that we don't need to 
	//	keep looking up model using the string.
	
	//	TODO:
	//	therefore, we can discard the string, and 
	//	probably resolve the model pointer in the 
	//	parse phase.  This will reduce runtime memory usage.
	//	however, no lookup is guaranteed complete in the parse phase,
	//	so run in init,  We still need the model name, but can discard it
	//	after lookup.
	if(m_pXForms)
	{
		if(!!m_sBind)
		{
			hr = m_pXForms->getBind(m_sBind,m_pBind);
			if(SUCCEEDED(hr))
			{
				setModel(m_pBind->m_pModel);
				m_pBind->AddControl(this);
				if(m_vertices.IsEmpty())
				{
					onBindToNothing();
				}
			}
			else if(hr == E_ABORT)
			{
				m_pXForms->m_lstInitPending.AddTail(this);
			}
		}
		else
		{
			hr = getModel();
			if(hr == E_ABORT)
			{
				m_pXForms->m_lstInitPending.AddTail(this);
			}
		}
	}

	if (SUCCEEDED(hr) && m_bisBound && m_pModel)
	{
		bool bAddedToList = false;
		if(!m_pBind)
		{
			if(m_pParent && m_pParent->m_bisBound)
			{
				bAddedToList = ((CImplNodeBinding*)m_pParent)->AddControl(this);
			}
			else if(m_pParentImpl)
			{
				CImplNodeBinding * pContextParent = NULL;
				// Bug 465 - Get the context parent for the element.
				hr = getContextParent(&pContextParent);

				if(pContextParent && !IsAwaitingDeletion())
					bAddedToList = pContextParent->AddControl(this);
			}
		}	
	}

	//if the initialisation of this control so far has 
	//	led to the control being bound, display the bound data.
	//This could happen if the control is added late, through DOM
	//	or repeat iteration etc.
	
	if(m_pXForms && m_vertices.GetUpperBound() != -1)
	{
		POSITION pos = m_Children.GetStartPosition();
		while(pos)
		{
			CImpl * pChild = NULL;
			m_Children.GetNextAssoc(pos, pChild, pChild);
			if(pChild)
			{
				CImplNodeBinding * pChildBinding = dynamic_cast<CImplNodeBinding *>(pChild);
				if(pChildBinding)
				{
					pChildBinding->buildDependencies();
				}
			}
		}
	}

	return hr;	
}



// A control may not receive focus if it is bound to an irrelevant or non-existant node
bool CImplNodeBinding::receiveFocus(void)
{
	bool bRet = false;
	int iSize = m_vertices.GetSize();
	if(iSize == 0)
	{	
		//bound to nothing, behave as irrelevant.
		bRet = false;
	}
	else
	{
		//ensure that the node(s) pointed to are relevant
		//if some nodes, but not all are relevant in a nodeset binding 
		//	succeed anyway, the individual object should probably
		//	handle this behaviour.  It may wish to set focus
		//	to one of the relevant parts of itself.
	
		for(long i = 0; i < iSize; ++i)
		{
			CVertex * pVX = m_vertices[i];

			if(pVX->m_pInstanceNode && pVX->m_pInstanceNode->isRelevant())
			{
				//see what base classes have to say about it.
				bRet = __super::receiveFocus();
				break;
			}
		}
	}
	return bRet;
}

HRESULT CImplNodeBinding::getContextParent(CImplNodeBinding ** pContextParent)
{
	HRESULT hr = S_FALSE;
	CImpl * pParent = m_pParentImpl;
	CImpl * pNext = 0;

	// If there is no parent when lets try and resolve
	// its parent now.
	if(!pParent)
		if(m_pXFormsElement)
			m_pXFormsElement->ResolveEnclosingBinding();
	
	// While we have a parent and it not a CImplNodeBinding
	// as we can get context from a CImplNodeBinding object
	while(pParent && !(pParent->m_bisBound && dynamic_cast<CImplNodeBinding*>(pParent))) {
		pNext = pParent->m_pParentImpl;
		// If the next parent is null then as long as it has a 
		// Behaviour when resolves it parent by crawling the HTML tree.
		if(!pNext) {
			if(pParent->m_pXFormsElement) {
				pParent->m_pXFormsElement->ResolveEnclosingBinding();
				pNext = pParent->m_pParentImpl;
			}
		}
		// Set the next parent to the current
		pParent = pNext;
	}

	// Before setting the Context Parent we must confirm that
	// it is a valid pointer i.e. is a CImplNodeBinding.
	*pContextParent = dynamic_cast<CImplNodeBinding*>(pParent);
	if(*pContextParent)
	{
		hr = S_OK;
	}
	else
	{
		CImplModel * pModel = 	dynamic_cast<CImplModel*>(pParent);
		if(pModel)
		{
			setModel(pModel);
		}
	}

	return hr;
}

bool CImplNodeBinding::IsAppropriateToInitializeNow()
{
	bool isAppropriate;

	if(m_pXFormsElement && !m_pXFormsElement->m_bElementRendered)
	{
		//elementReady hasn't got to the rendering part yet.
		isAppropriate = false;
	}
	else if(m_bInParentContext)
	{
		CFormControl * pParentFC = dynamic_cast<CFormControl*>(m_pParentImpl);
		
		if(!pParentFC)
			//Archaic: We should eradicate m_pParent, but until I get
			//	a good long period where it can be systematically removed and
			//	the results tested, I doubt it ever will be.
			pParentFC = dynamic_cast<CFormControl*>(m_pParent);

		if(pParentFC) 
			isAppropriate = (pParentFC->m_bInitializeDone || pParentFC->InitializeInProgress());
		else
			//Uncertain: It should be impossible to reach this point
			//	but if it does, and false is returned, then the control
			//	will never be initialised.
			//	However, Being in parent context but not having a parentImpl
			isAppropriate = true;
	}
	else
	{
		isAppropriate = true;
	}
	return isAppropriate;
}